﻿using System;
using System.Windows;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using Bling.DSL;
using Bling.WPF;
using Bling.Util;
using Bling.Physics;
using Bling.Animation;
using Bling.Shapes;

namespace Bling.PhysicsTutorial {
  internal static class PopcornExtensions {
    public static DoubleBl Stage(this PhysicsBlock<Popcorn.CustomBlock>.Body body) {
      return body.GetProperty(body.Block.StageProperty);
    }
  }
  public class Popcorn : PhysicsCanvas {
    public class CustomBlock : PhysicsBlock<CurvedPolygon, CurvedPolygonBl, CustomBlock>,
      ParticleBody.Is<CustomBlock>, RigidBody.Is<CustomBlock>, ShapeBody.Is<CustomBlock>, MouseAdapter.Is<CurvedPolygon, CustomBlock>, CollisionBody.Is<CustomBlock> {
      public readonly PhysicsProperty<double> StageProperty;
      public CustomBlock(World World, int Count, Func<int, CurvedPolygon> MakeT)
        : base(World, Count, MakeT) {
        StageProperty = NewBodyProperty<double>("Stage");
        new ParticleBody.Impl<CustomBlock>(this) { Loss = .1 };
        new RigidBody.Impl<CustomBlock>(this);
        new CollisionBody.Impl<CustomBlock>(this) {
          Mass = body => body.Stage(),
        };
        new ShapeBody.Impl<CustomBlock>(this) {
          ShapeStrength = body => 1d,
        };
        //this.MakeMouseAdapterE();
        new MouseAdapter.Impl<CurvedPolygon, CustomBlock>(this);
        this.ShapeBodyE().RelaxNext = (block0) => {
          block0.ForAll(body => {
            body.Rotation().Relax[body.Stage() > 0 & body.Stage() < 1] += .2.ToPI();
            body.Rotation().Relax[.05, body.Stage() == 1d] = .0.ToPI();
          });
        };
      }
      protected override void Init() {
        ForAll(body => {
          for (int i = 0; i < 4; i++) {
            body.Particles()[i].Position.Link.To = body.At.Points[i];
          }
          body.Stage().Relax[body.Stage() > 0] =
            (body.Stage() + .05).Min(1);


          body.Scale().Bind = .1 + .9 * (body.Stage().Ease.Bounce.In);
          body.Particles().ForAll(p => {
            PointBl r = new Random();
            p.Position.Init = (new PointBl(500, 500) * r);
            p.Position.Relax[body.Stage() < 1] += new PointBl(0, 2);
          });
        });
        base.Init(); 
      }
      protected override void Activate() {
        base.Activate();
      }
      static CustomBlock() {
        ParticleBody.Impl<CustomBlock>.ParticlesCount = 4;
      }

    }
    public Popcorn() : base("Popcorn", 30) {
      Background = Brushes.LightGray;
    }
    protected override Action Init(CanvasBl MainCanvas, World World) {
      var Count = 50;
      var canvas0 = new CanvasBl(MainCanvas) {
        LeftTop = Shake * -1d,
        Size = MainCanvas.Size,
      };
      var Pop = new ButtonBl(MainCanvas) {
        LeftTop = new PointBl(0, 0), ZIndex = 100,
        Content = "Pop",
        Font = { Size = 30 },
      };
      var UnPop = new ButtonBl(MainCanvas) {
        LeftTop = Pop.RightTop, ZIndex = 100,
        Content = "UnPop",
        Font = { Size = 30 },
      };

      CustomBlock block = new CustomBlock(World, Count, (idx) => {
        var ret = new CurvedPolygonBl(canvas0, 4) {
          Fill = idx.SelectColor(), Smoothness = 0,
        };
        return ret;
      }) { Name = "CI", };

      // create a collision manager
      var Collisions = new BodyCollisionManager(canvas0, World);

      block.ShapePoints().BindRect(new PointBl(100,100));
      block.CustomInit = (block0) => {
        block0.ForAll((CustomBlock.Body body) => {
          body.Particles().ForAll(particle => {
            var p = particle.Position;
            p.Relax[MainCanvas.Size.Length > 0] = p.Clamp(new PointBl(0, 0) + Shake, MainCanvas.Size + Shake);
            //p.Relax[body.Stage() <= 0 & MainCanvas.Size.Length > 0] = p % MainCanvas.Size;
          });
        });
      };

      return () => {
        Random r = new Random();
        IntBl Idx = ((IntBl)r) % block.Count;
        IntBl Jdx = ((IntBl)r) % 4;
        PointBl P = r;
        //var ComputePop = PointBl.Func<int>(i => block[i].Particles()[0].Position - new PointBl(0, MainCanvas.Width) + (P * new PointBl(2, .5) + new PointBl(0, .5)) * MainCanvas.Size);

        var GetStage = DoubleBl.Func<int>(i => block[i].Stage());
        var SetStage = DoubleBl.Assign<int,double>((i,d) => block[i].Stage(), (i,d) => d);
        var DoPop = PointBl.Assign<int>(i => block[i].Particles()[Jdx].Position.Old, i => {
          var Q = P * MainCanvas.Size;
          var W = block[i].Particles()[Jdx].Position;
          var D = Q - W; // will be negative
          return W - (D * 1d);


          //return new PointBl(-MainCanvas.Width + block[i].Particles()[Jdx].Position.X, MainCanvas.Height) + (P * new PointBl(2, .5) + new PointBl(0, 0.5)) * MainCanvas.Size;
        });
        int Popped = 0;
        Pop.Click = () => {
          while (true) {
            var i = r.Next(block.Count);
            if (GetStage(i) != 0) continue;
            DoPop(i);
            SetStage(i, .05);
            Popped += 1;
            break;
          }
          Pop.IsEnabled = Popped < block.Count;
        };
        UnPop.Click = () => {
          for (int i = 0; i < block.Count; i++)
            SetStage(i, 0);
          Popped = 0;
          Pop.IsEnabled = Popped < block.Count;
        };

        Collisions.Add(block);
        World.AfterRelax.Add(() => {});
      };
    }
  }

}
